home *** CD-ROM | disk | FTP | other *** search
-
- ; Keyin/Display/Clock Interface Routines.
- ;
- ; These routines provide a standard interface to the keyboard and
- ; display of the IBM personal computer.
- ;
- ; Copyright 1983 by John Cole, Duncanville, Texas.
- ;
- ;
- ; This routine is the interface to the display of the machine. It
- ; interprets a string of characters terminated by either a C$EOS
- ; character or a carriage return, sending the non-control characters
- ; to the display, and interpreting the controls to perform various
- ; functions.
- ;
- ; ON ENTRY:
- ; SI -- ADDRESS OF STRING TO BE DISPLAYED.
- ;
- ; ON EXIT:
- ; SI -- POINTS PAST LAST CHARACTER DISPLAYED.
- ; DL -- INDETERMINATE.
- ; AL -- INDETERMINATE.
- ;
- ; Equates for the various control characters
- ;
- C$NUL EQU 00H ; NULL CHARACTER
- C$EOS EQU 03H ; DISPLAY STRING TERMINATOR
- C$BELL EQU 07H ; BELL CHARACTER
- C$CPOS EQU 09H ; CURSOR POSITION (H,V) FOLLOWS
- C$CR EQU 0DH ; END WITH CARRIAGE RETURN, LINE FEED
- C$LF EQU 0AH ; LINE FEED
- C$BS EQU 08H ; BACKSPACE
- C$EEOF EQU 011H ; ERASE TO END OF FRAME
- C$EEOL EQU 012H ; ERASE TO END OF LINE
- C$RU EQU 013H ; SCROLL THE SCREEN UP
- C$RD EQU 014H ; SCROLL THE SCREEN DOWN
- C$ESC EQU 01BH ; ESCAPE/CANCEL
- ;
- ;Macro Definitions
- ;
- SVC MACRO FUNC
- MOV AH,FUNC
- INT 21H
- ENDM
- ;
- BIOS MACRO FUNC
- INT 10H
- ENDM
- ;
- ;
- ;
- S$DSPLY PROC NEAR ; DISPLAY PROCEDURE
- PUSH BX ; SAVE REGISTERS
- ;
- S$DSPLY0:
- MOV DL,[SI] ; GET THE CHARACTER
- INC SI ; MOVE THE POINTER
- CMP DL,C$EOS ; SEE IF END OF STRING
- JNE S$DSPLY1 ; BRANCH IF NOT
- POP BX ; RESTORE REGISTERS
- RET ; ELSE EXIT
- ;
- ; HERE CHECK FOR THE VARIOUS CONTROL CHARACTERS, AND TAKE APPROPRIATE
- ; ACTION IF WE GET THEM. THE FIRST CODE IS SCROLL UP.
- ;
- S$DSPLY1: CMP DL,C$RU ; SEE IF ROLL UP
- JNE S$DSPLY1A ; IF NOT, TRY SOMETHING ELSE
- ;
- MOV CX,0 ; SET TOP OF SCREEN
- MOV DX,2479H ; SET BOTTOM OF SCREEN WINDOW
- MOV BH,7 ; ATTRIBUTE OF BLANK LINE
- MOV AX,601H ; SET SCROLL CODE FOR BIOS, 1 LINE
- BIOS
- JMP S$DSPLY0 ; LOOP BACK FOR NEXT CHARACTER
- ;
- ; CHECK FOR SCROLL DOWN.
- ;
- S$DSPLY1A:
- CMP DL,C$RD ; CHECK FOR ROLL-DOWN CODE
- JNE S$DSPLY2 ; JUMP IF NOT
- MOV CX,0 ; SET TOP POSITION
- MOV DX,2479H ; AND BOTTOM OF SCREEN
- MOV BH,7 ; ATTRIBUTES OF BLANK LINE
- MOV AX,701H ; SCROLL 1 LINE, BIOS CODE IN AH
- BIOS ; DO IT
- JMP S$DSPLY0 ; BACK FOR NEXT CHARACTER
- ;
- ; CHECK FOR CURSOR POSITONING HERE.
- ;
- S$DSPLY2:
- CMP DL,C$CPOS ; CHECK IT
- JNE S$DSPLY3 ; JUMP IF NOT EQUAL
- MOV AH,2 ; FUNCTION NUMBER
- MOV DX,[SI] ; GET POSITION
- ADD SI,2 ; MOVE PAST THE POSITION
- MOV BH,0 ; SET DISPLAY PAGE
- BIOS ; CALL BIOS FOR DISPLAY CONTROL
- JMP S$DSPLY0 ; LOOP BACK FOR NEXT CHARACTER
- ;
- ; CHECK FOR CARRIAGE RETURN, WHICH DOES A LINE FEED AS WELL. THIS IS
- ; ALSO TREATED AS A STRING TERMINATOR.
- ;
- S$DSPLY3:
- CMP DL,C$CR ; CHECK FOR CARRIAGE RETURN
- JNE S$DSPLY4 ; JUMP IF NOT
- SVC DSPCHAR$ ; ELSE PUT OUT THE CHARACTER
- POP BX ; RESTORE REGISTERS
- RET ; AND EXIT
- ;
- ; ERASE SCREEN FROM CURRENT CURSOR POSITION TO END OF FRAME. THE
- ; LOGIC IS TO READ THE CURRENT CURSOR POSITION, THEN WRITE BLANKS TO
- ; THE SCREEN FROM THERE TO THE END OF THE SCREEN. FINALLY, WE RESET
- ; THE CURSOR POSITION TO ITS FORMER VALUE.
- ;
- S$DSPLY4:
- CMP DL,C$EEOF ; CHECK FOR ERASE TO END OF FRAME CODE
- JNE S$DSPLY5 ; IF NOT, TRY NEXT ONE
- ;
- MOV AH,3 ; CODE FOR READING THE CURSOR POSITION
- BIOS ; GET IT
- PUSH DX ; AND STACK IT
- CALL S$SCLR ; CLEAR THE REMAINDER OF THIS LINE
- POP DX ; RESTORE ORIGINAL CURSOR POSITION
- MOV AH,2 ; SET ORIGINAL CURSOR POSITION
- BIOS
- JMP S$DSPLY0 ; AND LOOP BACK
- ;
- ; CHECK FOR ERASE TO END OF LINE CODE. IF SO, JUST CALL THE ROUTINE
- ; TO CLEAR THE END OF THE LINE, THEN RESTORE THE CURRENT CURSOR.
- ;
- S$DSPLY5:
- CMP DL,C$EEOL ; CHECK FOR THE CODE
- JNE S$DSPLY8 ; IF NOT, TRY SOMETHING ELSE
- ;
- MOV AH,3 ; READ CURSOR POSITION
- BIOS ; GET IT
- PUSH DX ; AND STACK IT
- CALL S$DCLR ; CLEAR THE REST OF THIS LINE
- POP DX ; RESTORE OLD CURSOR POSITION
- MOV AH,2 ;
- BIOS ;
- JMP S$DSPLY0 ; AND BACK TO THE MAIN LOOP
- ;
- ; CHECK FOR BELL CHARACTER, CAUSES BEEP.
- ;
- S$DSPLY8:
- CMP DL,C$BELL ; TRY BELL CHARACTER
- JNE S$DSPLY9 ; JUMP IF NOT
- ;
- CALL S$BEEP ; BEEP THE SPEAKER
- JMP S$DSPLY0 ; AND RETURN TO THE LOOP
- ;
- ; HERE THE CHARACTER IS NOT ONE OF THE CONTROLS, SO JUST OUTPUT IT
- ; TO THE SCREEN, THEN LOOP.
- ;
- S$DSPLY9: SVC DSPCHAR$ ; DISPLAY THE CHARACTER
- JMP S$DSPLY0 ; AND LOOP BACK
- ;
- ;
- ; SUBROUTINE TO CLEAR A SINGLE ROW FROM THE POSITION IN DL TO THE
- ; END OF THE LINE. ON EXIT, DL HAS ZERO.
- ;
- S$DCLR:
- PUSH DX ; SAVE POSITION OVER LOOP
- MOV DH,80 ; SET MAXIMUM VALUE
- SUB DH,DL ; COMPUTE NUMBER OF COLUMNS TO CLEAR
- MOV DL,' ' ; FILL CHARACTER
- ;
- S$DCLR1:
- SVC DSPCHAR$ ; WRITE THE CHARACTER
- DEC DH ; DECREMENT THE COUNTER
- JNZ S$DCLR1 ; LOOP BACK IF NOT DONE
- POP DX ; RESTORE THE POSITION
- MOV DL,0 ; CLEAR COLUMN NUMBER
- RET ; AND EXIT
- S$DSPLY ENDP
- ;
- ; SUBROUTINE TO CLEAR THE SCREEN FROM THE CURSOR POSITION TO THE END OF
- ; THE SCREEN.
- ;
- S$SCLR: PUSH CX ; SAVE SOME REGISTERS
- PUSH ES ;
- PUSH DI ;
- MOV AL,DH ; GET CURSOR ROW
- PUSH DX ; STACK CURSOR POSITION
- MOV DX,160 ; NUMBER OF BYTES IN A ROW
- MOV AH,0 ; CLEAR HIGH BYTE
- MUL DL ; COMPUTE OFFSET FOR ROW
- POP DX ; RESTORE POSITION
- MOV DH,0 ; CLEAR HIGH BYTE OF COLUMN
- SAL DX,1 ; COMPUTE IN WORDS
- ADD AX,DX ; COMPUTE OFFSET
- MOV DI,AX ; COPY TO PROPER REGISTER
- PUSH DI ; STACK BUFFER OFFSET
- INT 11H ; GET EQUIPMENT TYPE
- MOV DI,AX ; COPY IT
- MOV AX,0B800H ; SEGMENT ADDRESS FOR COLOR CARD
- AND DI,30H ; SEE IF COLOR OR BLACK AND WHITE CARD
- CMP DI,30H ;
- JNE S$SCLR1 ; JUMP IF COLOR
- MOV AX,0B000H ; SET UP BLACK AND WHITE
- S$SCLR1: POP DI ; RESTORE BUFFER OFFSET
- MOV ES,AX ; SET UP CARD BASE AS EXTRA SEGMENT
- MOV CX,80*25*2 ; FILL LENGTH IN WORDS
- SUB CX,DI ; SUBTRACT OFF LENGTH BEFORE POSITION
- MOV AX,' '+7*256 ; FILL CHARACTER AND ATTRIBUTE
- REP STOSW ; STORE IT ALL
- POP DI ; RESTORE REGISTERS
- POP ES ;
- POP CX ;
- RET ; BACK TO CALLER
- ;
- PAGE
- ;
- ; CHARACTER DISPLAY SUBROUTINE.
- ;
- ; THIS ROUTINE PUTS THE CHARACTER ON THE SCREEN. HOWEVER, IF IT WOULD
- ; GO PAST THE END OF THE LINE, NO WRAPAROUND TAKES PLACE.
- ;
- S$DSPCHR PROC NEAR ; ENTRY POINT FOR CHARACTER DISPLAY
- PUSH BX ; STACK ATTRIBUTE
- PUSH CX ; AND COUNTER REGISTER
- PUSH DX ;
- MOV CX,1 ; ONLY ONE CHARACTER GOING OUT
- MOV BH,0 ; DISPLAY PAGE IN USE
- MOV AH,9 ; DISPLAY CHARACTER/ATTRIBUTE
- INT 10H ; CALL THE ROM
- MOV AH,3 ; GET CURRENT CURSOR POSITION
- INT 10H ;
- CMP DL,79 ; CHECK FOR PAST END OF LINE
- JE S$DSPCHR1 ;
- INC DL ; IF NOT, MOVE TO NEXT COLUMN
- MOV AH,2 ; NOW SET THE CURSOR
- INT 10H ;
- S$DSPCHR1: POP DX ;
- POP CX ; RESTORE REGISTERS
- POP BX ; . . .
- RET ; AND BACK TO CALLER
- S$DSPCHR ENDP ; END OF CHARACTER DISPLAY PROCEDURE
- ;
- ;
- ; RETURN CURRENT CURSOR POSITION.
- ;
- S$CURPOS PROC NEAR ; GET CURSOR POSITION IN DX
- PUSH CX ; STACK OVER CALL
- PUSH BX ;
- MOV BH,0 ; SET THE DISPLAY PAGE
- MOV AH,3 ; VIDEO I/O PARAMETER
- INT 10H ; CALL THE ROM
- POP BX ; RESTORE CALLER'S BX
- POP CX ; RESTORE CALLER'S CX
- RET ; EXIT WITH POSITION
- S$CURPOS ENDP ; END OF PROCEDURE
- ;
- ;
- PAGE
- ;
- ; Routine to key in a line of a specified length into a buffer.
- ;
- ; On entry:
- ; SI -- Address of buffer.
- ; CX -- Number of characters to accept.
- ;
- ; On Exit:
- ; SI -- Unchanged.
- ;
- S$KEYIN PROC NEAR ; PROCEDURE ENTRY POINT
- PUSH SI ; STACK BUFFER ADDRESS
- ;
- S$KEYIN0: SVC KEYINX$ ; GET A CHARACTER
- ;
- ; CHECK FOR BACKSPACE AND CANCEL HERE.
- ;
- CMP AL,C$ESC ; CHECK FOR ESCAPE
- JE S$KEYINA ; JUMP IF SO
- CMP AL,C$BS ; TEST WHAT WE GOT
- JNE S$KEYIN3 ; IF NOT, WAS SOMETHING ELSE
- ;
- S$KEYINA: MOV BP,SP ; GET STACK POINTER
- MOV DH,AL ; COPY CHARACTER THAT GOT US HERE
- S$KEYINB: CMP SI,[BP] ; CHECK FOR BEGINNING OF LINE
- JE S$KEYIN0 ; LOOP BACK IF SO
- MOV DL,C$BS ; GET A BACKSPACE
- SVC DSPCHAR$ ; BACK OVER CHARACTER
- MOV DL,' ' ; GET BLANK FOR CURRENT POSITION
- SVC DSPCHAR$ ; PUT IT OUT
- MOV DL,C$BS ; NOW BACKSPACE AGAIN
- SVC DSPCHAR$ ; SINCE WE HAVE PUT OUT THE BLANK
- INC CX ; AND INCREMENT THE COUNTER
- DEC SI ; BACK UP STORAGE POINTER
- CMP DH,C$ESC ; CHECK FOR ESCAPE/CANCEL
- JE S$KEYINB ; LOOP BACK IF SO
- JMP S$KEYIN0 ; AND LOOP BACK FOR THE NEXT ONE
- ;
- S$KEYIN3:
- MOV [SI],AL ; STORE IT
- INC SI ; MOVE THE POINTER
- CMP AL,C$CR ; CHECK FOR CARRIAGE RETURN
- JNE S$KEYIN4 ; JUMP IF NOT
- POP SI ; RESTORE STRING ADDRESS
- RET ; ELSE EXIT IF END OF LINE
- ;
- S$KEYIN4: MOV DL,AL ; COPY THE CHARACTER
- SVC DSPCHAR$ ; PUT IT ON THE SCREEN
- LOOPNZ S$KEYIN0 ; LOOP BACK FOR NEXT CHARACTER
- ;
- ; HERE THE COUNTER HAS DECREMENTED TO ZERO, SO WE SIT IN A HARD LOOP
- ; WAITING FOR A CARRIAGE RETURN.
- ;
- S$KEYIN5: SVC KEYINX$ ; GET A CHARACTER
- CMP AL,C$CR ; CHECK FOR END
- JE S$KEYIN6 ; EXIT IF IT IS
- CMP AL,C$BS ; CHECK FOR BACKSPACE
- JE S$KEYINA ; TAKE CARE OF THAT IF SO
- CMP AL,C$ESC ; CHECK FOR ESCAPE, TREATED AS CANCEL
- JE S$KEYINA ; AND JUMP IF SO
- CALL S$BEEP ; BEEP
- MOV DL,C$BS ; GET BACKSPACE
- SVC DSPCHAR$ ; AND BACK OVER BAD CHARACTER
- JMP S$KEYIN5 ; AND LOOP BACK
- ;
- S$KEYIN6: MOV [SI],AL ; STORE THE TERMINATOR
- POP SI ; RESTORE STRING ADDRESS
- RET ; AND EXIT
- S$KEYIN ENDP
- ;
- PAGE
- ; BEEP ROUTINE
- ;
- ; THIS ROUTINE CAUSES A BEEP AT THE SPEAKER OF THE PC.
- ;
- TIMER$ EQU 40H ; TIMER PORT
- PORT$B EQU 61H ; 8255 PORT B ADDRESS
- ;
- S$BEEP PROC NEAR ; START OF PROCEDURE
- MOV AL,10110110B ; GET TIMER SETTING
- OUT TIMER$+3,AL ; SET TIMER MODE
- MOV AX,533H ; DIVISOR FOR 1000 HZ
- OUT TIMER$+2,AL ; SET COUNT LSB
- MOV AL,AH ;
- OUT TIMER$+2,AL ; SET COUNT MSB
- IN AL,PORT$B ; GET CURRENT PORT MODE
- MOV AH,AL ; SAVE IT
- OR AL,3 ; TURN SPEAKER ON
- OUT PORT$B,AL
- MOV CX,32762 ; COUNTER FOR ABOUT 250 MS WAIT
- S$BEEP1: LOOP S$BEEP1 ; DELAY BEFORE TURNING OFF
- MOV AL,AH ; RECOVER OLD PORT VALUE
- OUT PORT$B,AL ; SET IT THE WAY IT WAS
- RET ; AND EXIT TO CALLER
- S$BEEP ENDP ; END OF ROUTINE
- ;
- PAGE
- ;
- ; GET TIME AS A STRING.
- ;
- ; This routine returns a string containing the current time as read
- ; from the system clock. The string is of the form HH:MM:SS.
- ;
- ; On entry:
- ; SI -- Address into which string is to be placed.
- ;
- ; On Exit:
- ; AX -- Changed.
- ;
- S$TIME PROC NEAR ; START OF TIME PROCEDURE
- PUSH SI ; STACK INCOMING REGISTERS
- PUSH DX ; . . .
- PUSH CX ; . . .
- SVC TIME$ ; GET THE TIME
- PUSH DX ; STACK SECONDS
- MOV AL,CH ; THE HOURS ARE GOOD
- CBW ; CONVERT TO WORD
- MOV DL,10 ; SEPARATE THE DIGITS
- DIV DL ;
- ADD AX,'00' ; CONVERT TO ASCII
- MOV [SI],AX ; SAVE HOURS
- MOV BYTE PTR 2[SI],':' ; STORE SEPARATOR
- ADD SI,3 ; MOVE STORE POINTER
- MOV AL,CL ; NOW GET THE MINUTES
- CBW ; CONVERT
- MOV DL,10 ; GET DIVISOR
- DIV DL ; SPLIT THE DIGITS
- ADD AX,'00' ; CONVERT TO ASCII
- MOV [SI],AX ; STORE IT, LSB,MSB
- MOV BYTE PTR 2[SI],':' ; AND KEEP THEM APART
- ADD SI,3 ; MOVE MICKEY'S BIG HAND
- POP DX ; RESTORE SECONDS
- MOV AL,DH ; SECONDS, PLEASE
- CBW ; CLEAR HIGH BYTE OF DIVIDEND
- MOV DL,10 ; DIVISOR
- DIV DL ; SPLIT
- ADD AX,'00' ; CONVERT TO ASCII
- MOV [SI],AX ; STORE
- POP CX ; RESTORE CALLER'S REGISTERS
- POP DX ; . . .
- POP SI ; . . .
- RET ; AND GO HOME
- S$TIME ENDP ; END OF TIME CONVERSION
- ;
- PAGE
- ;
- ; DATE CONVERSION ROUTINE.
- ;
- ; This routine gets the system date and converts it into a string of
- ; the form MM/DD/YY.
- ;
- ; On Entry:
- ; SI -- Address into which date is to be returned.
- ;
- ; On Exit:
- ; AX -- Changed.
- ; All other registers preserved.
- ;
- S$DATE PROC NEAR ; DATE CONVERSION PROCEDURE
- PUSH SI ; STACK CALLER'S REGISTERS
- PUSH CX ; . . .
- PUSH DX ; . . .
- SVC DATE$ ; CALL UP THE DATE
- PUSH DX ; SAVE THE DAY
- MOV AL,DH ; GET THE MONTH
- CBW ; EXPAND IT OUT
- MOV DL,10 ; GET DIVISOR
- DIV DL ; CONVERT MONTH
- ADD AX,'00' ; CONVERT TO ASCII
- MOV [SI],AX ; STORE IT
- MOV BYTE PTR 2[SI],'/' ; STORE SEPARATOR
- ADD SI,3 ; MOVE TO DAY
- POP DX ; RESTORE FROM STACK
- MOV AL,DL ; WHAT DAY IS IT?
- CBW ; EXPAND IT
- MOV DL,10 ; DIVISOR
- DIV DL ; SPLIT THE DIGITS APART
- ADD AX,'00' ; CONVERT TO ASCII
- MOV [SI],AX ; STORE IT
- MOV BYTE PTR 2[SI],'/' ; STORE SEPARATOR
- ADD SI,3 ; MOVE TO YEAR
- SUB CX,1900 ; SUBTRACT BIAS FROM YEAR
- CMP CX,100 ; SEE IF TURN OF MILLENIUM PAST
- JL S$DATE1 ; JUMP IF NOT (HIGH PROBABILITY)
- SUB CX,100 ; ELSE MAKE SURE WE GET JUST 2 DIGITS
- S$DATE1: MOV AL,CL ; GET THE YEAR
- CBW ; CLEAR HIGH BYTE
- MOV DL,10 ; DIVISOR
- DIV DL ; SPLIT
- ADD AX,'00' ; CONVERT
- MOV [SI],AX ; STORE
- POP DX ; RESTORE CALLER'S REGISTERS
- POP CX ; . . .
- POP SI ; . . .
- RET ; EXIT TO CALLER
- S$DATE ENDP ; THE DATE IS OVER
-